package org.easyupdater;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Set;

import org.apache.log4j.Logger;
import org.easyupdater.model.BundleSpec;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.service.packageadmin.PackageAdmin;

/**
 * Implementation of the OSGi operations
 * 
 * @author Bogdan Dimitriu
 */
public class OsgiBundleHandler implements IOsgiBundleHandler {

	private static final Logger log = Logger.getLogger(OsgiBundleHandler.class);

	private BundleContext bundleContext;
	
	private PackageAdmin packageAdmin;

	public OsgiBundleHandler() {
	}

	public boolean installBundle(BundleSpec bundleSpec) {
		try {
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(bundleSpec
					.getLocalJar()));
			bundleContext.installBundle(bundleSpec.getId(), bis);
			log.info("Bundle installation complete");
			bis.close();
			return true;
		} catch (FileNotFoundException e) {
			log.error("Could not find the bundle file: "
					+ bundleSpec.getLocalJar().getAbsolutePath(), e);
			return false;
		} catch (BundleException e) {
			log.error("Bundle installation failed", e);
			return false;
		} catch (IOException e) {
			log.error("IO problem", e);
			return false;
		}
	}

	public boolean removeBundle(BundleSpec bundleSpec) {
		Bundle[] allBundles = bundleContext.getBundles();
		for (Bundle bundle : allBundles) {
			if (bundle.getSymbolicName().equals(bundleSpec.getId())) {
				try {
					bundle.uninstall();
					boolean deleted = bundleSpec.getLocalJar().delete();
					if (deleted) {
						log.debug("Bundle deleted successfully: " + bundleSpec.getId());
					} else {
						log.warn("Bundle " + bundleSpec.getId() + " could not be deleted");
					}
				} catch (BundleException e) {
					log.error("Could not uninstall bundle: " + bundleSpec.getId(), e);
					return false;
				}
				return true;
			}
		}
		log.warn("Could not find any bundle with name " + bundleSpec.getId() + " to uninstall");
		return false;
	}

	public BundleContext getBundleContext() {
		return bundleContext;
	}

	public void setBundleContext(BundleContext bundleContext) {
		this.bundleContext = bundleContext;
	}

	public boolean startBundles(Set<BundleSpec> bundles) {
		Bundle[] allBundles = bundleContext.getBundles();
		for (BundleSpec bundleSpec : bundles) {
			for (Bundle bundle : allBundles) {
				if (bundle.getSymbolicName().equals(bundleSpec.getId())) {
					try {
						bundle.start();
					} catch (BundleException e) {
						log.error("Could not start bundle " + bundleSpec.getId() + "-"
								+ bundleSpec.getVersion());
						return false;
					}
				}
			}
		}
		log.info("Refreshing packages");
		// TODO refresh only the application packages
		packageAdmin.refreshPackages(null);
		return true;
	}

	public void stopBundles(Set<BundleSpec> bundles) {
		for (BundleSpec bundleSpec : bundles) {
			Bundle bundle = getBundleForName(bundleSpec.getId());
			if (bundle == null) {
				log.error("The bundle " + bundleSpec.getId()
						+ " is not installed in this OSGi container");
				// TODO what should we do in this case?
			} else {
				if (bundle.getState() != Bundle.ACTIVE) {
					log.warn("Not stopping bundle " + bundleSpec.getId()
							+ " as its current state is not ACTIVE");
				} else {
					try {
						bundle.stop();
						// TODO we probably need to stop the bundles that depend on the bundles
						// to be replaced
					} catch (BundleException e) {
						log.error("Cannot stop bundle " + bundleSpec.getId(), e);
					}
				}

			}
		}
	}

	/**
	 * Convenience method for getting the reference to a bundle as the OSGi API
	 * doesn't expose such a method
	 * 
	 * @param bundleName
	 *            the name of the bundle that we're looking for
	 * @return the reference to the bundle with the given name or null if such a
	 *         bundle doesn't exist
	 */
	protected Bundle getBundleForName(String bundleName) {
		Bundle[] allBundles = bundleContext.getBundles();
		for (Bundle bundle : allBundles) {
			if (bundle.getSymbolicName().equals(bundleName)) {
				return bundle;
			}
		}
		return null;
	}

	public PackageAdmin getPackageAdmin() {
		return packageAdmin;
	}

	public void setPackageAdmin(PackageAdmin packageAdmin) {
		this.packageAdmin = packageAdmin;
	}
}
